package top.dotgo.web.base.ex;

import cn.hutool.core.util.ReUtil;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import top.dotgo.kit.CfgKit;
import top.dotgo.kit.ContextKit;
import top.dotgo.kit.ex.DotException;
import top.dotgo.kit.ex.DotMsg;
import top.dotgo.web.base.R;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author jornl
 * @date 2020/3/9 15:08 星期一
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    public static final String THE_TOKEN_HAS_EXPIRED = "The Token has expired";
    public static final String REQUIRED_STRING_PARAMETER_SS_IS_NOT_PRESENT = "parameter '(.*?)' is not present";
    public static final String TOKEN_WAS_NOT_RECOGNISED = "Token was not recognised";
    public static final String USER_OR_PASSWORD_FAIL = "用户名或密码错误";
    public static final String ACC_DISABLE_LOGIN = "账号禁止登陆";
    public static final String INVALID_REFRESH_TOKEN = "Invalid refresh token";
    public static final String THE_TOKEN_WAS_EXPECTED_TO_HAVE_3_PARTS = "The token was expected to have 3 parts";
    public static final String REQUEST_METHOD_NOT_SUPPORTED = "Request method '(.*?)' not supported";
    public static final String STATUS_404_READING = "status 404 reading";

    /**
     * 转换 异常
     *
     * @param r r
     * @param e e
     */

    public static R getMsg(R r, Exception e) {
        if (r == null) {
            r = R.err(DotMsg.ERROR_CODE, DotMsg.ERROR_MSG);
        }
        if (e instanceof FeignException) {
            r.setCode(((FeignException) e).status());
            r.setMsg(e.getMessage() + ": " + ((FeignException) e).request().url().split("\\?")[0]);
        } else if (e instanceof DotException) {
            r.setCode(((DotException) e).getCode());
            if (((DotException) e).getMsg().contains(THE_TOKEN_WAS_EXPECTED_TO_HAVE_3_PARTS)) {
                r.setMsg("非法的token,请重新登录");
            } else {
                r.setMsg(((DotException) e).getMsg());
            }
        } else if (e.toString().contains(THE_TOKEN_HAS_EXPIRED)) {
            r.setCode(DotMsg.LOGIN_ERR);
            r.setMsg(DotMsg.LOGIN_ERR_MSG);
        } else if (e.toString().contains(TOKEN_WAS_NOT_RECOGNISED)) {
            r.setCode(DotMsg.LOGIN_ERR);
            r.setMsg("非法的token,请重新登录");
        } else if (e.toString().contains(INVALID_REFRESH_TOKEN)) {
            r.setCode(DotMsg.LOGIN_ERR);
            r.setMsg("非法的refresh_token,请重新登录");
        } else if (e.toString().contains(USER_OR_PASSWORD_FAIL)) {
            r.setCode(DotMsg.LOGIN_ERR);
            r.setMsg(USER_OR_PASSWORD_FAIL);
        } else if (e.toString().contains(ACC_DISABLE_LOGIN)) {
            r.setCode(DotMsg.LOGIN_ERR);
            r.setMsg(ACC_DISABLE_LOGIN);
        } else if (e.toString().contains(STATUS_404_READING)) {
            r.setCode(DotMsg.NOT_FOUND_ERR);
            r.setMsg(DotMsg.NOT_FOUND_MSG + ": " + ((HttpServletRequest) ContextKit.getRequest()).getRequestURI());
        } else {
            log.error("未知异常！原因是:", e);
            if (CfgKit.isDebug()) {
                r.setMsg("[ " + e.toString() + " ]          " + e.getStackTrace()[0].toString());
            } else {
                r.setMsg(e.getMessage());
            }
        }
        return r;
    }

    /**
     * 已知异常处理
     *
     * @param req req
     * @param e   e
     * @return {@link R}
     * @author jornl
     * @date 2020/3/9 17:24
     */
    @ExceptionHandler(value = DotException.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, DotException e) {
        R r = new R();
        r.setCode(e.getCode());
        if (CfgKit.isDebug()) {
            r.setMsg(e.getMsg() + "  " + e.getStackTrace()[0].toString());
        } else {
            r.setMsg(e.getMsg());
        }
        return r;
    }

    /**
     * 处理 validate异常
     *
     * @param req req
     * @param e   e
     * @return R
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, MethodArgumentNotValidException e) {
        StringBuilder msg = new StringBuilder();
        //解析原错误信息，封装后返回，此处返回非法的字段名称，原始值，错误信息
        for (FieldError error : e.getBindingResult().getFieldErrors()) {
            msg.append("[").append(error.getDefaultMessage()).append("]").append(",");
        }
        R r = new R();
        r.setCode(DotMsg.PARAM_ERR);
        r.setMsg(msg.toString());
        return r;
    }

    /**
     * 处理 参数缺少 异常
     *
     * @param req req
     * @param e   e
     * @return R
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, MissingServletRequestParameterException e) {
        R r = new R();
        r.setCode(DotMsg.PARAM_ERR);
        String msg = "缺少请求参数: " + ReUtil.findAll(REQUIRED_STRING_PARAMETER_SS_IS_NOT_PRESENT, e.getMessage(), 1).get(0);
        r.setMsg(msg);
        return r;
    }

    /**
     * 请求方式异常拦截
     *
     * @param req req
     * @param e   e
     * @return R
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, HttpRequestMethodNotSupportedException e) {
        R r = new R();
        r.setCode(DotMsg.PARAM_ERR);
        String msg = "请求方式错误: " + ReUtil.findAll(REQUEST_METHOD_NOT_SUPPORTED, e.getMessage(), 1).get(0);
        r.setMsg(msg);
        return r;
    }

    /**
     * 处理 Feign 404 异常
     *
     * @param req req
     * @param e   e
     * @return R
     */
    @ExceptionHandler(value = FeignException.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, FeignException e) {
        R r = new R();
        r.setCode(DotMsg.PARAM_ERR);
        String msg = "请求路径未找到: " + req.getRequestURI();
        r.setMsg(msg);
        return r;
    }

    /**
     * 方法参数类型不配
     *
     * @param req req
     * @param e   e
     * @return R
     */
    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, MethodArgumentTypeMismatchException e) {
        R r = new R();
        r.setCode(DotMsg.PARAM_ERR);

        List<String> list = ReUtil.findAll("For input string: \"'(.*?)'\"", e.getMessage(), 1);
        if (list != null && !list.isEmpty()) {
            String msg = "参数类型不匹配: " + list.get(0);
            r.setMsg(msg);
        } else {
            log.error(e.getMessage());
            String msg = "参数类型不匹配: " + e.getMessage();
            r.setMsg(msg);
        }

        return r;
    }

    /**
     * 参数校验异常
     *
     * @param req req
     * @param e   e
     * @return R
     */
    @ExceptionHandler(value = IllegalArgumentException.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, IllegalArgumentException e) {
        R r = new R();
        r.setCode(DotMsg.PARAM_ERR);
        String msg = e.getMessage();
        r.setMsg(msg);
        return r;
    }

    /**
     * 逻辑运算校验异常
     *
     * @param req req
     * @param e   e
     * @return R
     */
    @ExceptionHandler(value = IllegalStateException.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, IllegalStateException e) {
        R r = new R();
        r.setCode(DotMsg.PARAM_ERR);
        String msg = e.getMessage();
        r.setMsg(msg);
        return r;
    }




    /**
     * 处理其他异常
     *
     * @param req req
     * @param e   e
     * @return R
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public R exceptionHandler(HttpServletRequest req, Exception e) {
        return getMsg(null, e);
    }
}
